// Copyright 2019 谭杰鹏. All Rights Reserved //https://github.com/JiepengTan 

#if UNITY_EDITOR
using System;
using System.IO;
using System.Net;
using System.Net.Mime;
using System.Text;
using UnityEditor;
using UnityEngine;
using Debug = Lockstep.Logging.Debug;

namespace Lockstep.Math {
    public static class EditorCreateLUT {
        static string lutDir = Application.dataPath + "/Scripts/Engine.LockstepEngine/Math/LUT/";


        //[MenuItem("LockstepEngine/Math/TestAllLut")]
        static void TestAllLut(){
            TestSqrt(10000);
            TestSqrt(65535);
            TestSqrt(65536);
            TestSqrt(0xffffffff);
            TestSqrt(1000002L*1000001L);
            TestLutATan();
            TestATan2();
            TestLutASin();
            TestLutACos();
            TestLutSin();
            TestLutCos();
        }

        static void TestSqrt(ulong t){
            var val = LMath.Sqrt64(t);
            UnityEngine.Debug.Log($"sqrt({t}) = {val}" );
            return;
        }

        //[MenuItem("LockstepEngine/Math/CreateAllLUT")]
        static void CreateAllLUT(){
            CreateLutAtan2();
            CreateLUTASinCos();
            
        }

        //[MenuItem("LockstepEngine/Math/TestLutATan")]
        static void TestLutATan(){
            TestLut((i) => i, (i) => Mathf.Atan(i), (i) => new LFloat(true, LMath._LutATan(i.ToLFloat())));
        }

        static void TestLutASin(){
            TestLut((i) => i * 0.001f, (p) => Mathf.Asin(p), (p) => LMath.Asin(p.ToLFloat()));
        }

        static void TestLutACos(){
            TestLut((i) => i * 0.001f, (p) => Mathf.Acos(p), (p) => LMath.Acos(p.ToLFloat()));
        }

        static void TestLutSin(){
            TestLut((i) => i * 0.001f * Mathf.PI * 2 - Mathf.PI, (p) => Mathf.Sin(p), (p) => LMath.Sin(p.ToLFloat()));
        }

        static void TestLutCos(){
            TestLut((i) => i * 0.001f * Mathf.PI * 2 - Mathf.PI, (p) => Mathf.Cos(p), (p) => LMath.Cos(p.ToLFloat()));
        }

        static void TestLut(Func<int, float> funcPar, Func<float, float> rawFunc, Func<float, LFloat> lFunc){
            StringBuilder sb = new StringBuilder();
            int testSize1 = 1000;
            for (int i = 1; i < testSize1; i++) {
                var par = funcPar(i);
                var rawVal = rawFunc(par);
                var myVal = lFunc(par).ToFloat();
                var diff = rawVal - myVal;
                if (diff > 0.01f) {
                    sb.AppendLine($"i:{i} diff:{diff}");
                }
            }

            UnityEngine.Debug.Log(sb.ToString());
        }

        //[MenuItem("LockstepEngine/Math/TestATan2")]
        static void TestATan2(){
            StringBuilder sb = new StringBuilder();

            var v1 = Mathf.Atan2(1, 1);
            var v2 = Mathf.Atan2(1, -1);
            var v3 = Mathf.Atan2(-1, -1);
            var v4 = Mathf.Atan2(-1, 1);

            int testSize = 100;
            for (int y = -testSize; y < testSize; y++) {
                for (int x = -testSize; x < testSize; x++) {
                    var rawVal = Mathf.Atan2(y, x);
                    var myVal = new LFloat(true, LMath._Atan2(y, x)).ToFloat();
                    var diff = rawVal - myVal;
                    if (diff > 0.01f) {
                        sb.AppendLine($"y:{y} x:{x} diff:{diff}");
                    }
                }
            }

            UnityEngine.Debug.Log(sb.ToString());
        }

        private static string filePrefixStr = @"
//#define DONT_USE_GENERATE_CODE                                                              
//------------------------------------------------------------------------------              
// <auto-generated>                                                                           
//     This code was generated by Lockstep.CodeGenerator                                                            
//     Changes to this file may cause incorrect behavior and will be lost if                  
//     the code is regenerated.                                                               
//     https://github.com/JiepengTan/LockstepEngine                                         
// </auto-generated>                                                                          
//------------------------------------------------------------------------------  
";
        static void CreateLutAtan2(){
            var fileName = lutDir + "LUTAtan2.cs";
            string content = @"                                                                                       
using System;
using Lockstep.Math;
namespace Lockstep.Math
{
	public static class LUTAtan2
	{
        public const int MaxQueryIdx = $MAX_QUERY_IDX;//Abs(y/x)from 1 to MaxQueryIdx

        public static int[] _startIdx;
        public static int[] _arySize;
        public static int[] _tblTbl;

		static LUTAtan2()
		{
			_startIdx = new int[MaxQueryIdx +1]{
$START_IDX
			};
			_arySize = new int[MaxQueryIdx +1]{
$ARY_SIZE
			};
			_tblTbl = new int[]{
$ALL_VALUES
			};
		}
	}
}";
            const int MaxQueryIdx = 1000;
            const int interval = 1000;
            // UnityEngine.Debug.LogError("tan Pi/4 = " + Mathf.Tan(Mathf.PI / 4));
            // UnityEngine.Debug.LogError("atan 1 = " + Mathf.Atan(1) * Mathf.Rad2Deg);
            StringBuilder ssb = new StringBuilder();
            StringBuilder aryLne = new StringBuilder();
            int sum = 0;
            StringBuilder tblSb = new StringBuilder();
            StringBuilder startSb = new StringBuilder();
            StringBuilder arySizeSb = new StringBuilder();
            string sprefix = "\t\t\t\t";
            tblSb.Append(sprefix);
            startSb.Append(sprefix);
            arySizeSb.Append(sprefix);
            for (int i = 0; i < MaxQueryIdx; i++) {
                var val = Mathf.Atan(i + 1) * Mathf.Rad2Deg;
                var val2 = Mathf.Atan(i + 2) * Mathf.Rad2Deg;
                var arryLen = (int) ((val2 - val) * interval);
                if (arryLen == 0) {
                    arryLen = 1;
                }

                startSb.Append(sum + ", ");
                arySizeSb.Append(arryLen + ", ");
                for (int j = 0; j < arryLen; j++) {
                    var rad = Mathf.Atan(i + 1 + (j * 1.0f / arryLen));
                    tblSb.Append((int) (rad * LFloat.Precision) + ", ");
                    sum++;
                    if (sum % 100 == 0) {
                        tblSb.AppendLine();
                        tblSb.Append(sprefix);
                    }
                }

                if (i % 100 == 99) {
                    startSb.AppendLine();
                    arySizeSb.AppendLine();
                    startSb.Append(sprefix);
                    arySizeSb.Append(sprefix);
                }
            }

            startSb.Append("" + (sum + 1));
            arySizeSb.Append("" + 1);
            tblSb.Append("" + (int) (Mathf.PI / 4 * LFloat.Precision));
            content = content
                .Replace("$MAX_QUERY_IDX", MaxQueryIdx.ToString())
                .Replace("$START_IDX", startSb.ToString())
                .Replace("$ARY_SIZE", arySizeSb.ToString())
                .Replace("$ALL_VALUES", tblSb.ToString());

            //save to files
            File.WriteAllText(fileName, filePrefixStr+content);
            AssetDatabase.Refresh();
            return;
        }

        static void CreateLUTASinCos(){
            int ACount = 4000;
            CreateLUTA("LUTAsin", ACount, (i) =>
                Mathf.Asin(Mathf.Clamp(-1.0f + i * 2.0f / ACount, -1f, 1f))
            );
            CreateLUTA("LUTAcos", ACount, (i) =>
                Mathf.Acos(Mathf.Clamp(-1.0f + i * 2.0f / ACount, -1f, 1f))
            );
            int CCount = 7200;
            CreateLUTA("LUTCos", CCount, (i) =>
                Mathf.Cos(i * Mathf.PI * 2 / CCount)
            );
            CreateLUTA("LUTSin", CCount, (i) =>
                Mathf.Sin(i * Mathf.PI * 2 / CCount)
            );
        }

        static void CreateLUTA(string clsName, int _LutAryCount, Func<int, float> itemCallBack){
            var fileName = lutDir + clsName + ".cs";
            string content = @"using System;
using Lockstep.Math;
namespace Lockstep.Math
{
	public static class $CLS_NAME
	{
		public static readonly int COUNT;
		public static readonly int HALF_COUNT;
		public static readonly int[] table;
		static $CLS_NAME()
		{
			COUNT = $COUNT_VAL;
			HALF_COUNT = COUNT >> 1;
			table = new int[]
			{
$ALL_VALUES
			};
		}
	}
}";
            StringBuilder sb = new StringBuilder();
            string prefix = "\t\t\t\t";
            sb.Append(prefix);
            for (int i = 0; i <= _LutAryCount; i++) {
                int val = (int) (itemCallBack(i) * LFloat.Precision);
                sb.Append(val.ToString() + ",");
                if (i % 100 == 99) {
                    sb.AppendLine();
                    sb.Append(prefix);
                }
            }

            content = content
                .Replace("$CLS_NAME", clsName.ToString())
                .Replace("$COUNT_VAL", _LutAryCount.ToString())
                .Replace("$ALL_VALUES", sb.ToString());
            //save to files
            File.WriteAllText(fileName, filePrefixStr+content);
            AssetDatabase.Refresh();
        }
    }
}
#endif